﻿#region COPYRIGHT
//--------------------------------------------------------------------------------
// <copyright file="StaticPool.cs" company="starLiGHT Entertainment Studios">
//    Copyright (c) 2007, 2008, 2009, 2010 
//       Roland Rosenkranz (Glatzemann@email.de)
// </copyright>
//--------------------------------------------------------------------------------
#endregion

#region LICENSE

/*
    This file is part of starLiGHT.Collision.

    starLiGHT.Collision is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    starLiGHT.Collision is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with starLiGHT.Collision.  If not, see <http://www.gnu.org/licenses/>.

    
    ADDITIONAL (commercial) LICENSES for starLiGHT.Collision are available on request.
*/

#endregion

#region Version Stuff
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 620                   $:  Revision of last commit *
// * $Author:: unknown            $:  Author of last commit   *
// * $Date:: 2010-12-07 15:23:58 #$:  Date of last commit     *
// ************************************************************
#endregion

namespace starLiGHT.Collections
{
    #region Using Statements
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Text;

    #endregion

    /// <summary>
    /// Basic, abstract, generic pool for pooling objects. 
    /// A pool is usefull in situations where the cost of initializing a class instance is high,
    /// the rate of instantiation of a class is high and the number of instances in use at any 
    /// one time is low or if the garbage collection is slow like the one on the XBox360.
    /// The pooled object is obtained in predictable time when creation of the new objects may 
    /// take variable time. And there's no need of garbage collecting an object while it is stored
    /// in the pool. The garbage collection occurs only when the pool is destroyed.
    /// </summary>
    /// <remarks>This class is Thread-Safe. It uses locking.</remarks>
    /// <typeparam name="T">
    /// T must be a reference type (class) and must have a parameterless default constructor.
    /// T is the object, that is pooled by this pool.
    /// </typeparam>
    public abstract class StaticPool<T> where T : class, new()
    {
        private FastList<T> pool;
        private int poolCapacity;

        /// <summary>
        /// Initializes a new instance of the StaticPool class. It has a maximum capacity of 512 objects.
        /// </summary>
        /// <remarks>
        /// You should use the constructor with poolCapacity argument. The pool size should be set to
        /// a size that safes from unnecessary new allocations and don't wastes memory for unused objects. 
        /// A pool that is too big is better than a pool that is too small because the memory footprint of
        /// pool slots that are not used is small.
        /// </remarks>
        public StaticPool()
            : this(512)
        {
        }

        /// <summary>
        /// Initializes a new instance of the StaticPool class. Initialises the pool with a maximum number of object slots.
        /// </summary>
        /// <param name="initialPoolCapacity">
        /// The maximum size of the pool. The pool size should be set to
        /// a size that safes from unnecessary new allocations and don't wastes memory for unused objects. 
        /// A pool that is too big is better than a pool that is too small because the memory footprint of
        /// pool slots that are not used is small.
        /// </param>
        public StaticPool(int initialPoolCapacity)
        {
            this.pool = new FastList<T>();
            this.poolCapacity = initialPoolCapacity;
        }

        /// <summary>
        /// Gets the maximum capacity of the pool. This is the maximum number of pooled objects.
        /// </summary>
        public int PoolCapacity
        {
            get { return this.poolCapacity; }
        }

        /// <summary>
        /// This function is the heart of the pool. It is used to retrieve a object of the pool's 
        /// generic type. First it tries to return a unused object that is stored in the pool. If this
        /// fails, it allocates a new object. This object is not added to the pool until it is released.
        /// This safes the time for adding it to the pool's internal object storage at allocation time.
        /// </summary>
        /// <returns>Returns a object of the pool's generic type.</returns>
        /// <remarks>This function is thread-safe.</remarks>
        public virtual T Allocate()
        {
            T result = default(T);
            lock (this.pool)
            {
                this.pool.TryPopFront(out result);
            }

            if (result == null)
            {
                result = this.AllocateNew();
            }

            return result;
        }

        /// <summary>
        /// This function releases a object, that is allocated by the <see cref="Allocate"/> function. If
        /// the actual number of pooled objects is lower than <see cref="PoolCapacity"/> the object will
        /// be stored in the pool for future use. The next time you use <see cref="Allocate"/> one of this
        /// pooled objects is returned instead of a new one.
        /// </summary>
        /// <param name="obj">Object to be released.</param>
        /// <remarks>This function is thread-safe.</remarks>
        public virtual void Release(T obj)
        {
            lock (this.pool)
            {
                if (this.pool.Count <= this.PoolCapacity)
                {
                    this.pool.Add(obj);
                }
            }
        }

        /// <summary>
        /// Abstract function that is called if the pool needs a new instance of 
        /// a object. You should initialise the object to a valid default state.
        /// </summary>
        /// <returns>
        /// Returns a newly allocated T.
        /// </returns>
        protected abstract T AllocateNew();
    }
}